package com.eii.resubmit.aop.request.advisor;

import com.eii.resubmit.aop.request.Pair;
import com.eii.resubmit.aop.request.UAOP;
import com.eii.resubmit.aop.request.UServlet;
import com.eii.resubmit.aop.request.annotation.LimitResquest;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author eii
 * @sice 2020/11/30
 */
@Slf4j
@Component
public class LimitUserAdvisor implements PointcutAdvisor {
    /**
     * 限流器
     */
    private final Map<Method, Pair<RateLimiter, Long>> limitCache = new HashMap<>();
    /**
     * 执行器
     */
    private Advice advice = (MethodInterceptor) invocation -> {
        Pair<RateLimiter, Long> pair = limitCache.get(invocation.getMethod());
        if (!pair.getFirst().tryAcquire(pair.getSecond(), TimeUnit.MILLISECONDS)) {
            HttpServletRequest request = UServlet.request();
            log.info("服务器繁忙:IP地址{}", UServlet.getAddr(request));
            Map<String, String> resMap = new HashMap<>();
            resMap.put("result", "error:服务器繁忙");
            return resMap;
        }
        return invocation.proceed();
    };
    /**
     * 匹配器
     */
    private MethodMatcher methodMatcher = new MethodMatcher() {

        @Override
        public boolean matches(Method method, Class<?> targetClass, Object... args) {
            // isRuntime()方法返回值为false时,不会进行运行时判断
            return false;
        }

        @Override
        public boolean matches(Method method, Class<?> targetClass) {
            if (!UAOP.isController(targetClass)) {
                return false;
            }
            // 这里可以进行运行前检查
            LimitResquest annotation = LimitResquest.LimitResquestHelper.get(method, targetClass);
            if (annotation == null) {
                return false;
            }
            Pair<RateLimiter, Long> pair = new Pair<>(RateLimiter.create(annotation.value()), annotation.timeout());
            limitCache.put(method, pair);
            return true;
        }

        @Override
        public boolean isRuntime() {
            return false;
        }
    };

    private Pointcut pointcut = new Pointcut() {

        @Override
        public MethodMatcher getMethodMatcher() {
            return methodMatcher;
        }

        @Override
        public ClassFilter getClassFilter() {
            return clazz -> true;
        }

    };

    @Override
    public Pointcut getPointcut() {
        return pointcut;
    }

    @Override
    public Advice getAdvice() {
        return advice;
    }

    @Override
    public boolean isPerInstance() {
        return false;
    }
}
